package org.nicocube.airain.gwt.client.ui;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import org.nicocube.airain.domain.client.character.Action;
import org.nicocube.airain.domain.client.character.ActionType;
import org.nicocube.airain.domain.client.character.GameCharacter;
import org.nicocube.airain.domain.client.gamedate.GameDate;
import org.nicocube.airain.gwt.client.AirainWeb;
import org.nicocube.airain.gwt.client.rpc.ActionService;
import org.nicocube.airain.gwt.client.rpc.ActionServiceAsync;
import org.nicocube.airain.gwt.client.ui.dnd.CloneableActionWidget;

import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandlerAdapter;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.DropController;
import com.allen_sauer.gwt.dnd.client.drop.GridConstrainedDropController;
import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.Widget;

public class ActionSchedulerPanel extends AirainPanel {

	private FlowPanel myPanel; 
	private ActionServiceAsync plopSvc;
	private GameCharacter myGc;
	private AbsolutePanel boundaryPanel;
	private AbsolutePanel targetPanel;
	private Set<CloneableActionWidget> actionWidgets;
	private PickupDragController  dragController;
	private DropController dropController;

	public ActionSchedulerPanel(AirainWeb aw) {
		super(aw);
		// first We declare my panel
		myPanel = new FlowPanel();

		// then 
		plopSvc = GWT.create(ActionService.class);

		// set the referent GameCharacter
		myGc = getAW().getGc();

		Log.debug("building Action Set");
		actionWidgets = new HashSet<CloneableActionWidget>();

		Button b = new Button("save", new ClickListener() {
			public void onClick(Widget sender) {
				getAW().getMessager().show("Please Wait while saving.");
				saveSchedule(actionWidgets);
			}
		});

		myPanel.add(b);


		// Create a boundary panel to constrain all drag operations
		boundaryPanel = new AbsolutePanel();
		boundaryPanel.setWidth("100%");
		boundaryPanel.setHeight("400px");
		boundaryPanel.addStyleName("airain-dnd-boundary-blue");

		// Create a drop target on which we can drop labels
		targetPanel = new AbsolutePanel();
		targetPanel.setPixelSize(500, 360);
		targetPanel.addStyleName("airain-dnd-target-blue");

		// Add both panels to the flow panel
		myPanel.add(boundaryPanel);
		boundaryPanel.add(targetPanel, 20, 30);

		// Create a DragController for each logical area where a set of draggable
		// widgets and drop targets will be allowed to interact with one another.
		dragController = new PickupDragController(boundaryPanel, false);

		// Positioner is always constrained to the boundary panel
		// Use 'true' to also constrain the draggable or drag proxy to the boundary panel
		dragController.setBehaviorConstrainedToBoundaryPanel(false);

		// Allow multiple widgets to be selected at once using CTRL-click
		dragController.setBehaviorMultipleSelection(false);

		dragController.setBehaviorDragProxy(true);

		// create a DropController for each drop target on which draggable widgets
		// can be dropped
		//DropController dropController = new AbsolutePositionDropController(targetPanel);
		dropController = new GridConstrainedDropController(targetPanel, 100, 20);

		// Don't forget to register each DropController with a DragController
		dragController.registerDropController(dropController);

		dragController.addDragHandler(new DragHandlerAdapter() {			
			public void onDragEnd(DragEndEvent event) {

				CloneableActionWidget c = ((CloneableActionWidget)event.getContext().draggable);
				Log.debug("event.getContext().draggable:"+c.getType().toString());

				AbsolutePanel b = targetPanel;

				int hour = ((c.getAbsoluteTop() - b.getAbsoluteTop())/20)+1;
				int day = ((c.getAbsoluteLeft() - b.getAbsoluteLeft())/100)+1;

				Log.debug("drop : hour:"+hour+",day:"+day);

				// hour == 0 when the dropping is refused 
				if (hour > 0) {
					GameDate d;
					d = new GameDate(hour,day,1,1);

					Log.debug("GameDate : "+d);
					// the Action is to be created
					if (c.getAction() == null) {
						Log.debug("new Action created");
						actionWidgets.add(c);
						Action action = Action.build(((CloneableActionWidget)event.getSource()).getType(), getGameCharacter().getOrder(), d);
						c.setAction(action);
					}
					// the action has already been created we change the gameDate 
					else {
						Log.debug("we change existing Action GameDate");
						c.getAction().changeGameDate(d);
					}
				}

				Log.debug(Arrays.deepToString(filterActionWidget(actionWidgets).toArray()));

			}
			public void onPreviewDragEnd(DragEndEvent event)
			throws VetoDragException {		
				AbsolutePanel b = targetPanel;

				int hour = ((event.getContext().desiredDraggableY - b.getAbsoluteTop() +10)/20)+1;
				int day = ((event.getContext().desiredDraggableX - b.getAbsoluteLeft() +50)/100)+1;

				Log.debug("preview : hour:"+hour+",day:"+day);

				for (CloneableActionWidget c : actionWidgets) {
					GameDate d = c.getAction().getGameDate();
					if (hour == d.getHours() && day == d.getMoonDays()) throw new VetoDragException();	
				}
			}
		});

		HorizontalPanel actionPanel = new HorizontalPanel()  {
			@Override
			public boolean remove(Widget w) {
				int index = getWidgetIndex(w);
				if (index != -1 && w instanceof CloneableActionWidget) {
					CloneableActionWidget clone = ((CloneableActionWidget) w).cloneWidget();
					dragController.makeDraggable(clone);
					insert(clone, index);
				}
				return super.remove(w);
			}

		};
		actionPanel.setSpacing(5);

		boundaryPanel.add(actionPanel,30,0);

		int i=0;
		for (ActionType at : ActionType.values()) {
			// create a label and give it style		
			CloneableActionWidget label = new CloneableActionWidget(at);
			actionPanel.add(label);

			// make the label draggable
			dragController.makeDraggable(label);
			i++;
		}
	}

	protected Set<Action> filterActionWidget(Set<CloneableActionWidget> actionWidgets) {
		Set<Action> actions = new HashSet<Action>();
		for (CloneableActionWidget c : actionWidgets){
			actions.add(c.getAction());
		}
		return actions;
	}

	@Override
	public Widget getLabel() {
		return new Label("Action Scheduler",false);
	}

	@Override
	public Panel getPanel() {
		return myPanel;
	}

	private GameCharacter getGameCharacter() {
		return myGc;
	}

	@Override
	public TabListener getTabListener(final int index) {
		return new TabListener() {
			public boolean onBeforeTabSelected(SourcesTabEvents sender,	int tabIndex) {
				Log.debug("index="+index+",onBeforeTabSelected : "+tabIndex);
				if (tabIndex == index) {
					plopSvc.retrieveAction(getGameCharacter().getOrder(), new AsyncCallback<Set<Action>>(){
						public void onFailure(Throwable caught) {
							Log.error("problem while loading",caught);
							getAW().getMessager().show("problem while retrieving.");
						}
						public void onSuccess(Set<Action> result) {
							Log.debug(Arrays.deepToString(result.toArray()));
							fillSchedule(result);
						}
					});
				}
				return true;
			}
			public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
				Log.debug("index="+index+",onTabSelected : "+tabIndex);
			}
		};
	}

	private void fillSchedule(Set<Action> actions) {
		for(Action action : actions) {
			CloneableActionWidget c = new CloneableActionWidget(action.getType(),action);
			GameDate d = action.getGameDate();
			actionWidgets.add(c);
			targetPanel.add(c,(d.getMoonDays()-1)*100,(d.getHours()-1)*20);
			dragController.makeDraggable(c);
		}
	}


	private void saveSchedule(final Set<CloneableActionWidget> actionWidgets) {
		plopSvc.saveAction(filterActionWidget(actionWidgets), new AsyncCallback<Set<Action>>() {
			public void onFailure(Throwable caught) {
				Log.error("Error :",caught);
				getAW().getMessager().show("Error while saving.");
			}
			public void onSuccess(Set<Action> result) {
				Log.debug("Succeded :"+Arrays.deepToString(result.toArray()));
				getAW().getMessager().show("Saving complete.");
				LinkedList<CloneableActionWidget> toBeUpdated = new LinkedList<CloneableActionWidget>();
				for (CloneableActionWidget c : actionWidgets) {
					if (!c.getAction().isSaved()) {
						toBeUpdated.add(c);
					} else {
						result.remove(c.getAction());
					}
				}
				for (CloneableActionWidget c : toBeUpdated) {
					for (Action r : result) {
						Action a = c.getAction();
						if (r.getType().equals(a.getType())
								&& r.getGameCharacter().equals(a.getGameCharacter())
								&& r.getGameDate().equals(a.getGameDate()))
							c.setAction(r);
					}
				}
				getAW().getMessager().hide();
			}
		});

	}
}
